﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Xml.Linq;
using Galaktika.BI.Silverlight;
using HorizontalAlignment=System.Windows.HorizontalAlignment;
using VerticalAlignment=System.Windows.VerticalAlignment;
using Visibility=System.Windows.Visibility;
using System.Xml;
using System.IO;

namespace Galaktika.BI.Runtime
{
    public static class LayoutParser
    {
        /*
        public static List<GridLayoutWrapper> ParseLayout(string layout)
        {
            List<GridLayoutWrapper> list = new List<GridLayoutWrapper>();
            Size rootSize = new Size();
            XDocument doc = XDocument.Parse(layout);

            var items = from item in doc.Descendants().ToArray()[2].Descendants()
                        where item.FirstAttribute.Value.StartsWith("Item")
                        select item;

            foreach (var layoutItem in items)
            {
                var root = from c1 in layoutItem.Descendants()
                           where c1.FirstAttribute.Value == "Name"
                                 && c1.Value == GridLayoutWrapper.RootElement
                           select c1;
                if (root.Count()>0)
                {
                    var rootSizeItem = from b in layoutItem.Descendants()
                                       where b.FirstAttribute.Value == "Size"                                       
                                       select b.Value;
                    rootSize.Height = rootSizeItem.Count() > 0 ? ParsHeight(rootSizeItem.First()) : 0;
                    rootSize.Width = rootSizeItem.Count() > 0 ? ParsWidth(rootSizeItem.First()) : 0;
                }

                var nodes = from c in layoutItem.Descendants()
                            where c.FirstAttribute.Value == "TypeName"
                                  && c.Value == GridLayoutWrapper.LayoutItem
                            select c;

                if (nodes.Count() > 0)
                {
                    var node = from x in layoutItem.Descendants()
                               select x;
                    var wrapper = new GridLayoutWrapper();
                    foreach (var element in node)
                    {
                        switch (element.FirstAttribute.Value)
                        {
                            case "Name":
                                wrapper.Name = element.Value; break;
                            case "TypeName":
                                wrapper.LayoutTypeName = element.Value; break;
                            case "MinSize":
                                wrapper.MinHeight = ParsHeight(element.Value);
                                wrapper.MinWidth = ParsWidth(element.Value); break;
                            case "MaxSize":
                                wrapper.MaxHeight = ParsHeight(element.Value);
                                wrapper.MaxWidth = ParsWidth(element.Value); break;
                            case "Size":
                                wrapper.Height = ParsHeight(element.Value); 
                                wrapper.Width = ParsWidth(element.Value); break;
                            case "Location":
                                wrapper.Location_MarginTop = ParsY(element.Value);
                                wrapper.Location_MarginLeft = ParsX(element.Value); break;
                        }                                            
                                
                    }
                    wrapper.RootHeight = rootSize.Height;
                    wrapper.RootWidth = rootSize.Width;
                    list.Add(wrapper);
                    
                }

            }
                        
            return list;
        }

        private static double ParsX(string p)
        {
            return Parse(p, "X");
        }

        private static double Parse(string text, string propertyName)
        {
            double value = 0;
            int startIndex = 0;
            while (startIndex < text.Length)
            {
                int index = text.IndexOf("@", startIndex);
                if (index == -1)
                {
                    return value;
                }
                startIndex = index + 1;
                string[] strArray = text.Substring(startIndex).Split(new char[] { ',', '=' });
                if ((strArray == null) || (strArray.Length < 2))
                {
                    return value;
                }
                int length = Convert.ToInt32(strArray[0]);
                string str2 = strArray[1];
                index = (startIndex + text.Substring(startIndex).IndexOf("=")) + 1;               
                if (propertyName == str2)
                {
                    string str3 = text.Substring(index, length);
                    startIndex = index + length;
                    value = Convert.ToDouble(str3);
                }
            }
            return value;
        }

        private static double ParsY(string p)
        {
            return Parse(p, "Y");
        }

        private static double ParsHeight(string p)
        {
            return Parse(p, "Height");
        }

        private static double ParsWidth(string value)
        {
            return Parse(value, "Width");
        }
         
        public class GridLayoutWrapper
    {
        public static readonly string RootElement = "Root";
        public static readonly string LayoutGroup = "LayoutGroup";
        public static readonly string Separator = "SimpleSeparator";
        public static readonly string LayoutItem = "LayoutControlItem";

        public double Location_MarginLeft { get; set; }
        public double Location_MarginTop { get; set; }
        public double Width { get; set; }
        public double Height { get; set; }
        public Visibility Visibility { get; set; }
        public VerticalAlignment VerticalAlignment { get; set; }
        public HorizontalAlignment HorizontalAlignment { get; set; }
        public Style Style { get; set; }
        public string Name { get; set; }
        public double MinWidth { get; set; }
        public double MinHeight { get; set; }
        public double MaxWidth { get; set; }
        public double MaxHeight { get; set; }
        public string LayoutTypeName { get; set; }
        public double RootHeight { get; set; }
        public double RootWidth { get; set; }
    }

        */

        public static LayoutContainer Parse(string xml)
        {
            LayoutContainer lc = new LayoutContainer();

            // HACK: добавляем whitespace между узлами
            xml = xml.Replace("><", ">\r\n<");
            using (XmlReader xr = XmlReader.Create(new StringReader(xml)))
            {
                Stack<LayoutItemBase> items = new Stack<LayoutItemBase>();

                while (xr.Read())
                {
                    switch (xr.NodeType)
                    {
                        case XmlNodeType.Element:
                            if (xr.IsEmptyElement)
                            {
                                break;
                            }
                            switch (xr.Name)
                            {
                                case "property":
                                    {
                                        if (xr.MoveToAttribute("name"))
                                        {
                                            string name = xr.Value;
                                            if (name == "Items")
                                            {
                                                break;
                                            }
                                            else
                                            {
                                                if (name.StartsWith("Item", StringComparison.CurrentCultureIgnoreCase))
                                                {
                                                    LayoutItemBase item = new LayoutItemBase(name);
                                                    lc.Items.Add(item);
                                                    items.Push(item);
                                                }
                                                else
                                                {
                                                    if (items.Count > 0)
                                                    {
                                                        if (xr.MoveToContent() == XmlNodeType.Element)
                                                        {
                                                            items.Peek().Properties.Add(name, xr.ReadInnerXml());
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    break;
                                default:
                                    break;
                            }
                            break;
                        case XmlNodeType.EndElement:
                            // Вызывается так же для всякой шняги типа LookAndFeel
                            if (items.Count > 0)
                            {
                                items.Pop();
                            }
                            break;
                        default:
                            break;
                    }
                }
            }

            return lc;
        }
    }

    public class LayoutContainer
    {
        public LayoutContainer()
        {
            this.Items = new List<LayoutItemBase>();
        }

        public IList<LayoutItemBase> Items { get; private set; }
        private LayoutItemBase m_Root;
        public LayoutItemBase Root
        {
            get
            {
                if (m_Root == null)
                {
                    foreach (LayoutItemBase item in this.Items)
                    {
                        if (item.IsRoot)
                        {
                            m_Root = item;
                            break;
                        }
                    }
                }

                return m_Root;
            }
        }

        public LayoutItemBase this[string controlName]
        {
            get
            {
                foreach (LayoutItemBase item in this.Items)
                {
                    if (item.ControlName == controlName)
                    {
                        return item;
                    }
                }

                return null;
            }
        }
    }

    public class LayoutItemBase
    {
        public LayoutItemBase(string name)
        {
            this.Name = name;
            this.Properties = new Dictionary<string, string>();
        }

        private static double Parse(string text, string propertyName)
        {
            double value = 0;
            int startIndex = 0;
            while (startIndex < text.Length)
            {
                int index = text.IndexOf("@", startIndex);
                if (index == -1)
                {
                    return value;
                }
                startIndex = index + 1;
                string[] strArray = text.Substring(startIndex).Split(new char[] { ',', '=' });
                if ((strArray == null) || (strArray.Length < 2))
                {
                    return value;
                }
                int length = Convert.ToInt32(strArray[0]);
                string str2 = strArray[1];
                index = (startIndex + text.Substring(startIndex).IndexOf("=")) + 1;
                if (propertyName == str2)
                {
                    string str3 = text.Substring(index, length);
                    startIndex = index + length;
                    value = Convert.ToDouble(str3);
                }
            }
            return value;
        }

        private static Rect ParseRect(string rect)
        {
            if (string.IsNullOrEmpty(rect))
            {
                throw new ArgumentNullException("rect");
            }

            string[] rectParts = rect.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            if (rectParts.Length == 0)
            {
                throw new FormatException(string.Format(Localization.Exc_InvalidRectangle, rect));
            }

            if (rectParts.Length == 4)
            {
                return new Rect(
                    Convert.ToDouble(rectParts[0]),
                    Convert.ToDouble(rectParts[1]),
                    Convert.ToDouble(rectParts[2]),
                    Convert.ToDouble(rectParts[3]));
            }

            double size = Convert.ToDouble(rectParts[0]);
            return new Rect(size, size, size, size);
        }

        private static double ParseX(string p)
        {
            return Parse(p, "X");
        }

        private static double ParseY(string p)
        {
            return Parse(p, "Y");
        }

        private static double ParseHeight(string p)
        {
            return Parse(p, "Height");
        }

        private static double ParseWidth(string value)
        {
            return Parse(value, "Width");
        }

        private Size m_Size = default(Size);
        public Size Size
        {
            get
            {
                if (m_Size == default(Size))
                {
                    if (!this.Properties.ContainsKey("Size"))
                    {
                        return m_Size;
                    }
                    string size = this.Properties["Size"];
                    m_Size = new Size(ParseWidth(size), ParseHeight(size));
                }

                return m_Size;
            }
        }

        private Size m_MinSize = default(Size);
        public Size MinSize
        {
            get
            {
                if (m_MinSize == default(Size))
                {
                    if (!this.Properties.ContainsKey("MinSize"))
                    {
                        return m_MinSize;
                    }
                    string size = this.Properties["MinSize"];
                    m_MinSize = new Size(ParseWidth(size), ParseHeight(size));
                }

                return m_MinSize;
            }
        }

        private Size m_MaxSize = default(Size);
        public Size MaxSize
        {
            get
            {
                if (m_MaxSize == default(Size))
                {
                    if (!this.Properties.ContainsKey("MaxSize"))
                    {
                        return m_MaxSize;
                    }
                    string size = this.Properties["MaxSize"];
                    m_MaxSize = new Size(ParseWidth(size), ParseHeight(size));
                }

                return m_MaxSize;
            }
        }

        private Point m_Location = default(Point);
        public Point Location
        {
            get
            {
                if (m_Location == default(Point))
                {
                    if (!this.Properties.ContainsKey("Location"))
                    {
                        return m_Location;
                    }
                    string location = this.Properties["Location"];
                    m_Location = new Point(ParseX(location), ParseY(location));
                }

                return m_Location;
            }
        }

        private string m_ControlName = default(string);
        public string ControlName
        {
            get
            {
                if (m_ControlName == default(string))
                {
                    if (!this.Properties.ContainsKey("ControlName"))
                    {
                        return m_ControlName;
                    }
                    m_ControlName = this.Properties["ControlName"];
                }

                return m_ControlName;
            }
        }

        private Rect m_Padding = default(Rect);
        public Rect Padding
        {
            get
            {
                if (m_Padding == default(Rect))
                {
                    if (!this.Properties.ContainsKey("Padding"))
                    {
                        return m_Padding;
                    }
                    string padding = this.Properties["Padding"];
                    m_Padding = ParseRect(padding);
                }

                return m_Padding;
            }
        }

        public bool IsRoot
        {
            get
            {
                if (!this.Properties.ContainsKey("TypeName"))
                {
                    return false;
                }
                if (!this.Properties.ContainsKey("Name"))
                {
                    return false;
                }
                return this.Properties["TypeName"].Equals("LayoutGroup") &&
                    this.Properties["Name"].Equals("Root");
            }
        }

        public string Name { get; private set; }
        public IDictionary<string, string> Properties { get; private set; }
    }
}